{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "9db65c4f-8afe-4f36-9161-5c6d98ec668a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'当然可以！来一个关于冰淇淋的冷笑话（嘿嘿，冷笑话配冰淇淋刚刚好）：\\n\\n---\\n\\n有一天，一个冰淇淋走进了医院。\\n\\n医生问它：“你怎么了？”\\n\\n冰淇淋说：“我感觉我快**融化**了……”\\n\\n医生点点头，说：“那你得**冷静一下**。”\\n\\n---\\n\\n是不是很“冷”？😄🍦\\n\\n还想听更多吗？'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# LCEL(LangChain Expression Language) 的一个最简单示例\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"给我讲一个关于 {topic}的笑话\")\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "\n",
    ")\n",
    "output_parser = StrOutputParser()\n",
    "\n",
    "chain = prompt | model | output_parser\n",
    "\n",
    "chain.invoke({\"topic\": \"冰激凌\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0e30174e-4ef0-4d63-961b-299382db3ac1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "prompt_value： messages=[HumanMessage(content='给我讲一个关于 刺猬的笑话', additional_kwargs={}, response_metadata={})]\n",
      "[HumanMessage(content='给我讲一个关于 刺猬的笑话', additional_kwargs={}, response_metadata={})]\n",
      "Human: 给我讲一个关于 刺猬的笑话\n"
     ]
    }
   ],
   "source": [
    "# prompt\n",
    "prompt_value = prompt.invoke({\"topic\": \"刺猬\"})\n",
    "print(\"prompt_value：\", prompt_value)\n",
    "print(prompt_value.to_messages())\n",
    "print(prompt_value.to_string())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "f5c73dd2-424f-4371-977a-6dbbdf9f4bde",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "content='当然！来一个关于刺猬的冷笑话，轻松一下：\\n\\n有一天，刺猬去理发店理发。  \\n理发师一看：“哎呀，你这头发也太扎手了！”  \\n刺猬点点头：“没办法，我这是‘针’情难改啊！”\\n\\n……\\n\\n是不是有点“刺”激？😄' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 67, 'prompt_tokens': 21, 'total_tokens': 88, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-a5f0cb1d-9651-9bd4-8b12-d0d02bd00e83', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None} id='run--262e2bee-9733-4d14-be31-d66416d07db4-0' usage_metadata={'input_tokens': 21, 'output_tokens': 67, 'total_tokens': 88, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}\n"
     ]
    }
   ],
   "source": [
    "# model\n",
    "message = model.invoke(prompt_value)\n",
    "print(message)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "ae15ab89-0ecb-45cc-814f-835cd6c45472",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='当然！来一个关于刺猬的冷笑话，轻松一下：\\n\\n有一天，刺猬去理发店理发。  \\n理发师问它：“你想剪什么样的发型？”  \\n刺猬说：“随便剪，反正我头发竖着也是一根，横着也是一根。”\\n\\n😂\\n\\n想听更多刺猬的笑话也可以告诉我！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 69, 'prompt_tokens': 21, 'total_tokens': 90, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-983f1084-3cdc-9e9b-a4e7-ac57ad8195c3', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--36d96a6b-eee1-491c-9f4c-92a001d22ff8-0', usage_metadata={'input_tokens': 21, 'output_tokens': 69, 'total_tokens': 90, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 使用llm的区别\n",
    "from langchain_openai.llms import OpenAI\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "\n",
    ")\n",
    "llm.invoke(prompt_value)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "438738f1-56b1-4155-9c84-9906379ded47",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'当然！来一个关于刺猬的冷笑话，轻松一下：\\n\\n有一天，刺猬去理发店理发。  \\n理发师一看：“哎呀，你这头发也太扎手了！”  \\n刺猬点点头：“没办法，我这是‘针’情难改啊！”\\n\\n……\\n\\n是不是有点“刺”激？😄'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Output parser\n",
    "output_parser.invoke(message)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "af692dc6-3e01-4fc7-bbc0-9a43c5780914",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Harrison worked at Kensho.'"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"\"\"RAG Search Exampl\n",
    "    建立向量数据\n",
    "    使用RAG增强\n",
    "\"\"\"\n",
    "from operator import itemgetter\n",
    "\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_community.embeddings import DashScopeEmbeddings\n",
    "\n",
    "# 使用阿里云的嵌入模型\n",
    "embeddings = DashScopeEmbeddings(\n",
    "    model=\"text-embedding-v1\",  # 阿里云的嵌入模型\n",
    "    dashscope_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\"\n",
    ")\n",
    "vectorstore = FAISS.from_texts(\n",
    "    [\"harrison worked at kensho\"], embedding=embeddings\n",
    ")\n",
    "retriever = vectorstore.as_retriever()\n",
    "\n",
    "template = \"\"\"Answer the question based only on the following context:\n",
    "{context}\n",
    "\n",
    "Question: {question}\n",
    "\"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "\n",
    ")\n",
    "\n",
    "chain = (\n",
    "    {\"context\": retriever, \"question\": RunnablePassthrough()}\n",
    "    | prompt\n",
    "    | model\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "chain.invoke(\"where did harrison work?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "64f149ad-2a6b-4800-9415-782970520aa8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'harrison 在 kensho 工作过。'"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 自定义也非常简单\n",
    "template = \"\"\"Answer the question based only on the following context:\n",
    "{context}\n",
    "\n",
    "Question: {question}\n",
    "\n",
    "Answer in the following language: {language}\n",
    "\"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "\n",
    ")\n",
    "\n",
    "chain = (\n",
    "    {\n",
    "        \"context\": itemgetter(\"question\") | retriever,\n",
    "        \"question\": itemgetter(\"question\"),\n",
    "        \"language\": itemgetter(\"language\"),\n",
    "    }\n",
    "    | prompt\n",
    "    | model\n",
    "    | StrOutputParser()\n",
    ")\n",
    "chain.invoke({\"question\": \"where did harrison work\", \"language\": \"chinese\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "41a056af-9749-4abb-9fae-6977b06f2e97",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\27727\\AppData\\Local\\Temp\\ipykernel_15780\\1912167977.py:15: PydanticDeprecatedSince20: The `schema` method is deprecated; use `model_json_schema` instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.11/migration/\n",
      "  chain.input_schema.schema()\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'properties': {'topic': {'title': 'Topic', 'type': 'string'}},\n",
       " 'required': ['topic'],\n",
       " 'title': 'PromptInput',\n",
       " 'type': 'object'}"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    ")\n",
    "\n",
    "prompt = ChatPromptTemplate.from_template(\"给我讲一个关于{topic}的笑话\")\n",
    "chain = prompt | model\n",
    "\n",
    "#prompt.\n",
    "chain.input_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "baed0e5c-4d2e-4935-8490-a2140661d4d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\27727\\AppData\\Local\\Temp\\ipykernel_15780\\1594619117.py:1: PydanticDeprecatedSince20: The `schema` method is deprecated; use `model_json_schema` instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.11/migration/\n",
      "  prompt.input_schema.schema()\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'properties': {'topic': {'title': 'Topic', 'type': 'string'}},\n",
       " 'required': ['topic'],\n",
       " 'title': 'PromptInput',\n",
       " 'type': 'object'}"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prompt.input_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "4c6ae5e4-52b5-4677-83a7-4d1ca51c33eb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'$defs': {'AIMessage': {'additionalProperties': True,\n",
       "   'description': 'Message from an AI.\\n\\nAIMessage is returned from a chat model as a response to a prompt.\\n\\nThis message represents the output of the model and consists of both\\nthe raw output as returned by the model together standardized fields\\n(e.g., tool calls, usage metadata) added by the LangChain framework.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'ai',\n",
       "     'default': 'ai',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'example': {'default': False, 'title': 'Example', 'type': 'boolean'},\n",
       "    'tool_calls': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/ToolCall'},\n",
       "     'title': 'Tool Calls',\n",
       "     'type': 'array'},\n",
       "    'invalid_tool_calls': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/InvalidToolCall'},\n",
       "     'title': 'Invalid Tool Calls',\n",
       "     'type': 'array'},\n",
       "    'usage_metadata': {'anyOf': [{'$ref': '#/$defs/UsageMetadata'},\n",
       "      {'type': 'null'}],\n",
       "     'default': None}},\n",
       "   'required': ['content'],\n",
       "   'title': 'AIMessage',\n",
       "   'type': 'object'},\n",
       "  'AIMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Message chunk from an AI.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'AIMessageChunk',\n",
       "     'default': 'AIMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'example': {'default': False, 'title': 'Example', 'type': 'boolean'},\n",
       "    'tool_calls': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/ToolCall'},\n",
       "     'title': 'Tool Calls',\n",
       "     'type': 'array'},\n",
       "    'invalid_tool_calls': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/InvalidToolCall'},\n",
       "     'title': 'Invalid Tool Calls',\n",
       "     'type': 'array'},\n",
       "    'usage_metadata': {'anyOf': [{'$ref': '#/$defs/UsageMetadata'},\n",
       "      {'type': 'null'}],\n",
       "     'default': None},\n",
       "    'tool_call_chunks': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/ToolCallChunk'},\n",
       "     'title': 'Tool Call Chunks',\n",
       "     'type': 'array'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'AIMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'ChatMessage': {'additionalProperties': True,\n",
       "   'description': 'Message that can be assigned an arbitrary speaker (i.e. role).',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'chat',\n",
       "     'default': 'chat',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'role': {'title': 'Role', 'type': 'string'}},\n",
       "   'required': ['content', 'role'],\n",
       "   'title': 'ChatMessage',\n",
       "   'type': 'object'},\n",
       "  'ChatMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Chat Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'ChatMessageChunk',\n",
       "     'default': 'ChatMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'role': {'title': 'Role', 'type': 'string'}},\n",
       "   'required': ['content', 'role'],\n",
       "   'title': 'ChatMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'ChatPromptValueConcrete': {'description': 'Chat prompt value which explicitly lists out the message types it accepts.\\n\\nFor use in external schemas.',\n",
       "   'properties': {'messages': {'items': {'oneOf': [{'$ref': '#/$defs/AIMessage'},\n",
       "       {'$ref': '#/$defs/HumanMessage'},\n",
       "       {'$ref': '#/$defs/ChatMessage'},\n",
       "       {'$ref': '#/$defs/SystemMessage'},\n",
       "       {'$ref': '#/$defs/FunctionMessage'},\n",
       "       {'$ref': '#/$defs/ToolMessage'},\n",
       "       {'$ref': '#/$defs/AIMessageChunk'},\n",
       "       {'$ref': '#/$defs/HumanMessageChunk'},\n",
       "       {'$ref': '#/$defs/ChatMessageChunk'},\n",
       "       {'$ref': '#/$defs/SystemMessageChunk'},\n",
       "       {'$ref': '#/$defs/FunctionMessageChunk'},\n",
       "       {'$ref': '#/$defs/ToolMessageChunk'}]},\n",
       "     'title': 'Messages',\n",
       "     'type': 'array'},\n",
       "    'type': {'const': 'ChatPromptValueConcrete',\n",
       "     'default': 'ChatPromptValueConcrete',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'}},\n",
       "   'required': ['messages'],\n",
       "   'title': 'ChatPromptValueConcrete',\n",
       "   'type': 'object'},\n",
       "  'FunctionMessage': {'additionalProperties': True,\n",
       "   'description': 'Message for passing the result of executing a tool back to a model.\\n\\nFunctionMessage are an older version of the ToolMessage schema, and\\ndo not contain the tool_call_id field.\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'function',\n",
       "     'default': 'function',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'}},\n",
       "   'required': ['content', 'name'],\n",
       "   'title': 'FunctionMessage',\n",
       "   'type': 'object'},\n",
       "  'FunctionMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Function Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'FunctionMessageChunk',\n",
       "     'default': 'FunctionMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'}},\n",
       "   'required': ['content', 'name'],\n",
       "   'title': 'FunctionMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'HumanMessage': {'additionalProperties': True,\n",
       "   'description': 'Message from a human.\\n\\nHumanMessages are messages that are passed in from a human to the model.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        from langchain_core.messages import HumanMessage, SystemMessage\\n\\n        messages = [\\n            SystemMessage(\\n                content=\"You are a helpful assistant! Your name is Bob.\"\\n            ),\\n            HumanMessage(\\n                content=\"What is your name?\"\\n            )\\n        ]\\n\\n        # Instantiate a chat model and invoke it with the messages\\n        model = ...\\n        print(model.invoke(messages))',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'human',\n",
       "     'default': 'human',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'example': {'default': False, 'title': 'Example', 'type': 'boolean'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'HumanMessage',\n",
       "   'type': 'object'},\n",
       "  'HumanMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Human Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'HumanMessageChunk',\n",
       "     'default': 'HumanMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'example': {'default': False, 'title': 'Example', 'type': 'boolean'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'HumanMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'InputTokenDetails': {'description': 'Breakdown of input token counts.\\n\\nDoes *not* need to sum to full input token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        {\\n            \"audio\": 10,\\n            \"cache_creation\": 200,\\n            \"cache_read\": 100,\\n        }\\n\\n.. versionadded:: 0.3.9\\n\\nMay also hold extra provider-specific keys.',\n",
       "   'properties': {'audio': {'title': 'Audio', 'type': 'integer'},\n",
       "    'cache_creation': {'title': 'Cache Creation', 'type': 'integer'},\n",
       "    'cache_read': {'title': 'Cache Read', 'type': 'integer'}},\n",
       "   'title': 'InputTokenDetails',\n",
       "   'type': 'object'},\n",
       "  'InvalidToolCall': {'description': 'Allowance for errors made by LLM.\\n\\nHere we add an `error` key to surface errors made during generation\\n(e.g., invalid JSON arguments.)',\n",
       "   'properties': {'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'title': 'Name'},\n",
       "    'args': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Args'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Id'},\n",
       "    'error': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'title': 'Error'},\n",
       "    'type': {'const': 'invalid_tool_call', 'title': 'Type', 'type': 'string'}},\n",
       "   'required': ['name', 'args', 'id', 'error'],\n",
       "   'title': 'InvalidToolCall',\n",
       "   'type': 'object'},\n",
       "  'OutputTokenDetails': {'description': 'Breakdown of output token counts.\\n\\nDoes *not* need to sum to full output token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        {\\n            \"audio\": 10,\\n            \"reasoning\": 200,\\n        }\\n\\n.. versionadded:: 0.3.9',\n",
       "   'properties': {'audio': {'title': 'Audio', 'type': 'integer'},\n",
       "    'reasoning': {'title': 'Reasoning', 'type': 'integer'}},\n",
       "   'title': 'OutputTokenDetails',\n",
       "   'type': 'object'},\n",
       "  'StringPromptValue': {'description': 'String prompt value.',\n",
       "   'properties': {'text': {'title': 'Text', 'type': 'string'},\n",
       "    'type': {'const': 'StringPromptValue',\n",
       "     'default': 'StringPromptValue',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'}},\n",
       "   'required': ['text'],\n",
       "   'title': 'StringPromptValue',\n",
       "   'type': 'object'},\n",
       "  'SystemMessage': {'additionalProperties': True,\n",
       "   'description': 'Message for priming AI behavior.\\n\\nThe system message is usually passed in as the first of a sequence\\nof input messages.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        from langchain_core.messages import HumanMessage, SystemMessage\\n\\n        messages = [\\n            SystemMessage(\\n                content=\"You are a helpful assistant! Your name is Bob.\"\\n            ),\\n            HumanMessage(\\n                content=\"What is your name?\"\\n            )\\n        ]\\n\\n        # Define a chat model and invoke it with the messages\\n        print(model.invoke(messages))',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'system',\n",
       "     'default': 'system',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'SystemMessage',\n",
       "   'type': 'object'},\n",
       "  'SystemMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'System Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'SystemMessageChunk',\n",
       "     'default': 'SystemMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'SystemMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'ToolCall': {'description': 'Represents a request to call a tool.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        {\\n            \"name\": \"foo\",\\n            \"args\": {\"a\": 1},\\n            \"id\": \"123\"\\n        }\\n\\n    This represents a request to call the tool named \"foo\" with arguments {\"a\": 1}\\n    and an identifier of \"123\".',\n",
       "   'properties': {'name': {'title': 'Name', 'type': 'string'},\n",
       "    'args': {'additionalProperties': True, 'title': 'Args', 'type': 'object'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Id'},\n",
       "    'type': {'const': 'tool_call', 'title': 'Type', 'type': 'string'}},\n",
       "   'required': ['name', 'args', 'id'],\n",
       "   'title': 'ToolCall',\n",
       "   'type': 'object'},\n",
       "  'ToolCallChunk': {'description': 'A chunk of a tool call (e.g., as part of a stream).\\n\\nWhen merging ToolCallChunks (e.g., via AIMessageChunk.__add__),\\nall string attributes are concatenated. Chunks are only merged if their\\nvalues of `index` are equal and not None.\\n\\nExample:\\n\\n.. code-block:: python\\n\\n    left_chunks = [ToolCallChunk(name=\"foo\", args=\\'{\"a\":\\', index=0)]\\n    right_chunks = [ToolCallChunk(name=None, args=\\'1}\\', index=0)]\\n\\n    (\\n        AIMessageChunk(content=\"\", tool_call_chunks=left_chunks)\\n        + AIMessageChunk(content=\"\", tool_call_chunks=right_chunks)\\n    ).tool_call_chunks == [ToolCallChunk(name=\\'foo\\', args=\\'{\"a\":1}\\', index=0)]',\n",
       "   'properties': {'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'title': 'Name'},\n",
       "    'args': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Args'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Id'},\n",
       "    'index': {'anyOf': [{'type': 'integer'}, {'type': 'null'}],\n",
       "     'title': 'Index'},\n",
       "    'type': {'const': 'tool_call_chunk', 'title': 'Type', 'type': 'string'}},\n",
       "   'required': ['name', 'args', 'id', 'index'],\n",
       "   'title': 'ToolCallChunk',\n",
       "   'type': 'object'},\n",
       "  'ToolMessage': {'additionalProperties': True,\n",
       "   'description': 'Message for passing the result of executing a tool back to a model.\\n\\nToolMessages contain the result of a tool invocation. Typically, the result\\nis encoded inside the `content` field.\\n\\nExample: A ToolMessage representing a result of 42 from a tool call with id\\n\\n    .. code-block:: python\\n\\n        from langchain_core.messages import ToolMessage\\n\\n        ToolMessage(content=\\'42\\', tool_call_id=\\'call_Jja7J89XsjrOLA5r!MEOW!SL\\')\\n\\n\\nExample: A ToolMessage where only part of the tool output is sent to the model\\n    and the full output is passed in to artifact.\\n\\n    .. versionadded:: 0.2.17\\n\\n    .. code-block:: python\\n\\n        from langchain_core.messages import ToolMessage\\n\\n        tool_output = {\\n            \"stdout\": \"From the graph we can see that the correlation between x and y is ...\",\\n            \"stderr\": None,\\n            \"artifacts\": {\"type\": \"image\", \"base64_data\": \"/9j/4gIcSU...\"},\\n        }\\n\\n        ToolMessage(\\n            content=tool_output[\"stdout\"],\\n            artifact=tool_output,\\n            tool_call_id=\\'call_Jja7J89XsjrOLA5r!MEOW!SL\\',\\n        )\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'tool',\n",
       "     'default': 'tool',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'tool_call_id': {'title': 'Tool Call Id', 'type': 'string'},\n",
       "    'artifact': {'default': None, 'title': 'Artifact'},\n",
       "    'status': {'default': 'success',\n",
       "     'enum': ['success', 'error'],\n",
       "     'title': 'Status',\n",
       "     'type': 'string'}},\n",
       "   'required': ['content', 'tool_call_id'],\n",
       "   'title': 'ToolMessage',\n",
       "   'type': 'object'},\n",
       "  'ToolMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Tool Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'ToolMessageChunk',\n",
       "     'default': 'ToolMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'tool_call_id': {'title': 'Tool Call Id', 'type': 'string'},\n",
       "    'artifact': {'default': None, 'title': 'Artifact'},\n",
       "    'status': {'default': 'success',\n",
       "     'enum': ['success', 'error'],\n",
       "     'title': 'Status',\n",
       "     'type': 'string'}},\n",
       "   'required': ['content', 'tool_call_id'],\n",
       "   'title': 'ToolMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'UsageMetadata': {'description': 'Usage metadata for a message, such as token counts.\\n\\nThis is a standard representation of token usage that is consistent across models.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        {\\n            \"input_tokens\": 350,\\n            \"output_tokens\": 240,\\n            \"total_tokens\": 590,\\n            \"input_token_details\": {\\n                \"audio\": 10,\\n                \"cache_creation\": 200,\\n                \"cache_read\": 100,\\n            },\\n            \"output_token_details\": {\\n                \"audio\": 10,\\n                \"reasoning\": 200,\\n            }\\n        }\\n\\n.. versionchanged:: 0.3.9\\n\\n    Added ``input_token_details`` and ``output_token_details``.',\n",
       "   'properties': {'input_tokens': {'title': 'Input Tokens', 'type': 'integer'},\n",
       "    'output_tokens': {'title': 'Output Tokens', 'type': 'integer'},\n",
       "    'total_tokens': {'title': 'Total Tokens', 'type': 'integer'},\n",
       "    'input_token_details': {'$ref': '#/$defs/InputTokenDetails'},\n",
       "    'output_token_details': {'$ref': '#/$defs/OutputTokenDetails'}},\n",
       "   'required': ['input_tokens', 'output_tokens', 'total_tokens'],\n",
       "   'title': 'UsageMetadata',\n",
       "   'type': 'object'}},\n",
       " 'anyOf': [{'type': 'string'},\n",
       "  {'$ref': '#/$defs/StringPromptValue'},\n",
       "  {'$ref': '#/$defs/ChatPromptValueConcrete'},\n",
       "  {'items': {'oneOf': [{'$ref': '#/$defs/AIMessage'},\n",
       "     {'$ref': '#/$defs/HumanMessage'},\n",
       "     {'$ref': '#/$defs/ChatMessage'},\n",
       "     {'$ref': '#/$defs/SystemMessage'},\n",
       "     {'$ref': '#/$defs/FunctionMessage'},\n",
       "     {'$ref': '#/$defs/ToolMessage'},\n",
       "     {'$ref': '#/$defs/AIMessageChunk'},\n",
       "     {'$ref': '#/$defs/HumanMessageChunk'},\n",
       "     {'$ref': '#/$defs/ChatMessageChunk'},\n",
       "     {'$ref': '#/$defs/SystemMessageChunk'},\n",
       "     {'$ref': '#/$defs/FunctionMessageChunk'},\n",
       "     {'$ref': '#/$defs/ToolMessageChunk'}]},\n",
       "   'type': 'array'}],\n",
       " 'title': 'ChatOpenAIInput'}"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.input_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "527b08b6-f31f-48b0-b1e0-c3d7cbb533fe",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'$defs': {'AIMessage': {'additionalProperties': True,\n",
       "   'description': 'Message from an AI.\\n\\nAIMessage is returned from a chat model as a response to a prompt.\\n\\nThis message represents the output of the model and consists of both\\nthe raw output as returned by the model together standardized fields\\n(e.g., tool calls, usage metadata) added by the LangChain framework.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'ai',\n",
       "     'default': 'ai',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'example': {'default': False, 'title': 'Example', 'type': 'boolean'},\n",
       "    'tool_calls': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/ToolCall'},\n",
       "     'title': 'Tool Calls',\n",
       "     'type': 'array'},\n",
       "    'invalid_tool_calls': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/InvalidToolCall'},\n",
       "     'title': 'Invalid Tool Calls',\n",
       "     'type': 'array'},\n",
       "    'usage_metadata': {'anyOf': [{'$ref': '#/$defs/UsageMetadata'},\n",
       "      {'type': 'null'}],\n",
       "     'default': None}},\n",
       "   'required': ['content'],\n",
       "   'title': 'AIMessage',\n",
       "   'type': 'object'},\n",
       "  'AIMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Message chunk from an AI.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'AIMessageChunk',\n",
       "     'default': 'AIMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'example': {'default': False, 'title': 'Example', 'type': 'boolean'},\n",
       "    'tool_calls': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/ToolCall'},\n",
       "     'title': 'Tool Calls',\n",
       "     'type': 'array'},\n",
       "    'invalid_tool_calls': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/InvalidToolCall'},\n",
       "     'title': 'Invalid Tool Calls',\n",
       "     'type': 'array'},\n",
       "    'usage_metadata': {'anyOf': [{'$ref': '#/$defs/UsageMetadata'},\n",
       "      {'type': 'null'}],\n",
       "     'default': None},\n",
       "    'tool_call_chunks': {'default': [],\n",
       "     'items': {'$ref': '#/$defs/ToolCallChunk'},\n",
       "     'title': 'Tool Call Chunks',\n",
       "     'type': 'array'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'AIMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'ChatMessage': {'additionalProperties': True,\n",
       "   'description': 'Message that can be assigned an arbitrary speaker (i.e. role).',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'chat',\n",
       "     'default': 'chat',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'role': {'title': 'Role', 'type': 'string'}},\n",
       "   'required': ['content', 'role'],\n",
       "   'title': 'ChatMessage',\n",
       "   'type': 'object'},\n",
       "  'ChatMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Chat Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'ChatMessageChunk',\n",
       "     'default': 'ChatMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'role': {'title': 'Role', 'type': 'string'}},\n",
       "   'required': ['content', 'role'],\n",
       "   'title': 'ChatMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'FunctionMessage': {'additionalProperties': True,\n",
       "   'description': 'Message for passing the result of executing a tool back to a model.\\n\\nFunctionMessage are an older version of the ToolMessage schema, and\\ndo not contain the tool_call_id field.\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'function',\n",
       "     'default': 'function',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'}},\n",
       "   'required': ['content', 'name'],\n",
       "   'title': 'FunctionMessage',\n",
       "   'type': 'object'},\n",
       "  'FunctionMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Function Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'FunctionMessageChunk',\n",
       "     'default': 'FunctionMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'title': 'Name', 'type': 'string'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'}},\n",
       "   'required': ['content', 'name'],\n",
       "   'title': 'FunctionMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'HumanMessage': {'additionalProperties': True,\n",
       "   'description': 'Message from a human.\\n\\nHumanMessages are messages that are passed in from a human to the model.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        from langchain_core.messages import HumanMessage, SystemMessage\\n\\n        messages = [\\n            SystemMessage(\\n                content=\"You are a helpful assistant! Your name is Bob.\"\\n            ),\\n            HumanMessage(\\n                content=\"What is your name?\"\\n            )\\n        ]\\n\\n        # Instantiate a chat model and invoke it with the messages\\n        model = ...\\n        print(model.invoke(messages))',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'human',\n",
       "     'default': 'human',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'example': {'default': False, 'title': 'Example', 'type': 'boolean'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'HumanMessage',\n",
       "   'type': 'object'},\n",
       "  'HumanMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Human Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'HumanMessageChunk',\n",
       "     'default': 'HumanMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'example': {'default': False, 'title': 'Example', 'type': 'boolean'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'HumanMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'InputTokenDetails': {'description': 'Breakdown of input token counts.\\n\\nDoes *not* need to sum to full input token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        {\\n            \"audio\": 10,\\n            \"cache_creation\": 200,\\n            \"cache_read\": 100,\\n        }\\n\\n.. versionadded:: 0.3.9\\n\\nMay also hold extra provider-specific keys.',\n",
       "   'properties': {'audio': {'title': 'Audio', 'type': 'integer'},\n",
       "    'cache_creation': {'title': 'Cache Creation', 'type': 'integer'},\n",
       "    'cache_read': {'title': 'Cache Read', 'type': 'integer'}},\n",
       "   'title': 'InputTokenDetails',\n",
       "   'type': 'object'},\n",
       "  'InvalidToolCall': {'description': 'Allowance for errors made by LLM.\\n\\nHere we add an `error` key to surface errors made during generation\\n(e.g., invalid JSON arguments.)',\n",
       "   'properties': {'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'title': 'Name'},\n",
       "    'args': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Args'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Id'},\n",
       "    'error': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'title': 'Error'},\n",
       "    'type': {'const': 'invalid_tool_call', 'title': 'Type', 'type': 'string'}},\n",
       "   'required': ['name', 'args', 'id', 'error'],\n",
       "   'title': 'InvalidToolCall',\n",
       "   'type': 'object'},\n",
       "  'OutputTokenDetails': {'description': 'Breakdown of output token counts.\\n\\nDoes *not* need to sum to full output token count. Does *not* need to have all keys.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        {\\n            \"audio\": 10,\\n            \"reasoning\": 200,\\n        }\\n\\n.. versionadded:: 0.3.9',\n",
       "   'properties': {'audio': {'title': 'Audio', 'type': 'integer'},\n",
       "    'reasoning': {'title': 'Reasoning', 'type': 'integer'}},\n",
       "   'title': 'OutputTokenDetails',\n",
       "   'type': 'object'},\n",
       "  'SystemMessage': {'additionalProperties': True,\n",
       "   'description': 'Message for priming AI behavior.\\n\\nThe system message is usually passed in as the first of a sequence\\nof input messages.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        from langchain_core.messages import HumanMessage, SystemMessage\\n\\n        messages = [\\n            SystemMessage(\\n                content=\"You are a helpful assistant! Your name is Bob.\"\\n            ),\\n            HumanMessage(\\n                content=\"What is your name?\"\\n            )\\n        ]\\n\\n        # Define a chat model and invoke it with the messages\\n        print(model.invoke(messages))',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'system',\n",
       "     'default': 'system',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'SystemMessage',\n",
       "   'type': 'object'},\n",
       "  'SystemMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'System Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'SystemMessageChunk',\n",
       "     'default': 'SystemMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'}},\n",
       "   'required': ['content'],\n",
       "   'title': 'SystemMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'ToolCall': {'description': 'Represents a request to call a tool.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        {\\n            \"name\": \"foo\",\\n            \"args\": {\"a\": 1},\\n            \"id\": \"123\"\\n        }\\n\\n    This represents a request to call the tool named \"foo\" with arguments {\"a\": 1}\\n    and an identifier of \"123\".',\n",
       "   'properties': {'name': {'title': 'Name', 'type': 'string'},\n",
       "    'args': {'additionalProperties': True, 'title': 'Args', 'type': 'object'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Id'},\n",
       "    'type': {'const': 'tool_call', 'title': 'Type', 'type': 'string'}},\n",
       "   'required': ['name', 'args', 'id'],\n",
       "   'title': 'ToolCall',\n",
       "   'type': 'object'},\n",
       "  'ToolCallChunk': {'description': 'A chunk of a tool call (e.g., as part of a stream).\\n\\nWhen merging ToolCallChunks (e.g., via AIMessageChunk.__add__),\\nall string attributes are concatenated. Chunks are only merged if their\\nvalues of `index` are equal and not None.\\n\\nExample:\\n\\n.. code-block:: python\\n\\n    left_chunks = [ToolCallChunk(name=\"foo\", args=\\'{\"a\":\\', index=0)]\\n    right_chunks = [ToolCallChunk(name=None, args=\\'1}\\', index=0)]\\n\\n    (\\n        AIMessageChunk(content=\"\", tool_call_chunks=left_chunks)\\n        + AIMessageChunk(content=\"\", tool_call_chunks=right_chunks)\\n    ).tool_call_chunks == [ToolCallChunk(name=\\'foo\\', args=\\'{\"a\":1}\\', index=0)]',\n",
       "   'properties': {'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'title': 'Name'},\n",
       "    'args': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Args'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}], 'title': 'Id'},\n",
       "    'index': {'anyOf': [{'type': 'integer'}, {'type': 'null'}],\n",
       "     'title': 'Index'},\n",
       "    'type': {'const': 'tool_call_chunk', 'title': 'Type', 'type': 'string'}},\n",
       "   'required': ['name', 'args', 'id', 'index'],\n",
       "   'title': 'ToolCallChunk',\n",
       "   'type': 'object'},\n",
       "  'ToolMessage': {'additionalProperties': True,\n",
       "   'description': 'Message for passing the result of executing a tool back to a model.\\n\\nToolMessages contain the result of a tool invocation. Typically, the result\\nis encoded inside the `content` field.\\n\\nExample: A ToolMessage representing a result of 42 from a tool call with id\\n\\n    .. code-block:: python\\n\\n        from langchain_core.messages import ToolMessage\\n\\n        ToolMessage(content=\\'42\\', tool_call_id=\\'call_Jja7J89XsjrOLA5r!MEOW!SL\\')\\n\\n\\nExample: A ToolMessage where only part of the tool output is sent to the model\\n    and the full output is passed in to artifact.\\n\\n    .. versionadded:: 0.2.17\\n\\n    .. code-block:: python\\n\\n        from langchain_core.messages import ToolMessage\\n\\n        tool_output = {\\n            \"stdout\": \"From the graph we can see that the correlation between x and y is ...\",\\n            \"stderr\": None,\\n            \"artifacts\": {\"type\": \"image\", \"base64_data\": \"/9j/4gIcSU...\"},\\n        }\\n\\n        ToolMessage(\\n            content=tool_output[\"stdout\"],\\n            artifact=tool_output,\\n            tool_call_id=\\'call_Jja7J89XsjrOLA5r!MEOW!SL\\',\\n        )\\n\\nThe tool_call_id field is used to associate the tool call request with the\\ntool call response. This is useful in situations where a chat model is able\\nto request multiple tool calls in parallel.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'tool',\n",
       "     'default': 'tool',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'tool_call_id': {'title': 'Tool Call Id', 'type': 'string'},\n",
       "    'artifact': {'default': None, 'title': 'Artifact'},\n",
       "    'status': {'default': 'success',\n",
       "     'enum': ['success', 'error'],\n",
       "     'title': 'Status',\n",
       "     'type': 'string'}},\n",
       "   'required': ['content', 'tool_call_id'],\n",
       "   'title': 'ToolMessage',\n",
       "   'type': 'object'},\n",
       "  'ToolMessageChunk': {'additionalProperties': True,\n",
       "   'description': 'Tool Message chunk.',\n",
       "   'properties': {'content': {'anyOf': [{'type': 'string'},\n",
       "      {'items': {'anyOf': [{'type': 'string'},\n",
       "         {'additionalProperties': True, 'type': 'object'}]},\n",
       "       'type': 'array'}],\n",
       "     'title': 'Content'},\n",
       "    'additional_kwargs': {'additionalProperties': True,\n",
       "     'title': 'Additional Kwargs',\n",
       "     'type': 'object'},\n",
       "    'response_metadata': {'additionalProperties': True,\n",
       "     'title': 'Response Metadata',\n",
       "     'type': 'object'},\n",
       "    'type': {'const': 'ToolMessageChunk',\n",
       "     'default': 'ToolMessageChunk',\n",
       "     'title': 'Type',\n",
       "     'type': 'string'},\n",
       "    'name': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Name'},\n",
       "    'id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
       "     'default': None,\n",
       "     'title': 'Id'},\n",
       "    'tool_call_id': {'title': 'Tool Call Id', 'type': 'string'},\n",
       "    'artifact': {'default': None, 'title': 'Artifact'},\n",
       "    'status': {'default': 'success',\n",
       "     'enum': ['success', 'error'],\n",
       "     'title': 'Status',\n",
       "     'type': 'string'}},\n",
       "   'required': ['content', 'tool_call_id'],\n",
       "   'title': 'ToolMessageChunk',\n",
       "   'type': 'object'},\n",
       "  'UsageMetadata': {'description': 'Usage metadata for a message, such as token counts.\\n\\nThis is a standard representation of token usage that is consistent across models.\\n\\nExample:\\n\\n    .. code-block:: python\\n\\n        {\\n            \"input_tokens\": 350,\\n            \"output_tokens\": 240,\\n            \"total_tokens\": 590,\\n            \"input_token_details\": {\\n                \"audio\": 10,\\n                \"cache_creation\": 200,\\n                \"cache_read\": 100,\\n            },\\n            \"output_token_details\": {\\n                \"audio\": 10,\\n                \"reasoning\": 200,\\n            }\\n        }\\n\\n.. versionchanged:: 0.3.9\\n\\n    Added ``input_token_details`` and ``output_token_details``.',\n",
       "   'properties': {'input_tokens': {'title': 'Input Tokens', 'type': 'integer'},\n",
       "    'output_tokens': {'title': 'Output Tokens', 'type': 'integer'},\n",
       "    'total_tokens': {'title': 'Total Tokens', 'type': 'integer'},\n",
       "    'input_token_details': {'$ref': '#/$defs/InputTokenDetails'},\n",
       "    'output_token_details': {'$ref': '#/$defs/OutputTokenDetails'}},\n",
       "   'required': ['input_tokens', 'output_tokens', 'total_tokens'],\n",
       "   'title': 'UsageMetadata',\n",
       "   'type': 'object'}},\n",
       " 'oneOf': [{'$ref': '#/$defs/AIMessage'},\n",
       "  {'$ref': '#/$defs/HumanMessage'},\n",
       "  {'$ref': '#/$defs/ChatMessage'},\n",
       "  {'$ref': '#/$defs/SystemMessage'},\n",
       "  {'$ref': '#/$defs/FunctionMessage'},\n",
       "  {'$ref': '#/$defs/ToolMessage'},\n",
       "  {'$ref': '#/$defs/AIMessageChunk'},\n",
       "  {'$ref': '#/$defs/HumanMessageChunk'},\n",
       "  {'$ref': '#/$defs/ChatMessageChunk'},\n",
       "  {'$ref': '#/$defs/SystemMessageChunk'},\n",
       "  {'$ref': '#/$defs/FunctionMessageChunk'},\n",
       "  {'$ref': '#/$defs/ToolMessageChunk'}],\n",
       " 'title': 'ChatOpenAIOutput'}"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# The output schema of the chain is the output schema of its last part, in this case a ChatModel, which outputs a ChatMessage\n",
    "chain.output_schema.schema()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "2d6bb06f-be24-4e24-a199-f6d3c851fb55",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当然可以！给你讲一个关于熊的冷笑话：\n",
      "\n",
      "---\n",
      "\n",
      "有一天，一只熊走进了一家药店，问药剂师：“你们有蜂蜜吗？”\n",
      "\n",
      "药剂师说：“没有，我们不卖蜂蜜。”\n",
      "\n",
      "第二天，这只熊又来了，问：“你们有蜂蜜吗？”\n",
      "\n",
      "药剂师有点奇怪，但还是回答：“没有，我们真的不卖蜂蜜。”\n",
      "\n",
      "第三天，熊又来了，问：“你们有蜂蜜吗？”\n",
      "\n",
      "药剂师终于忍不住了，说：“我们从来都不卖蜂蜜，你为什么要一直问？”\n",
      "\n",
      "熊挠挠头说：“我只是想确认一下……我好像有点感冒了，想泡点蜂蜜水。”\n",
      "\n",
      "药剂师：？？？\n",
      "\n",
      "---\n",
      "\n",
      "是不是有点“熊”憨憨的～😄  \n",
      "还想听更多关于动物的笑话吗？"
     ]
    }
   ],
   "source": [
    "# Stream（流式）\n",
    "for s in chain.stream({\"topic\": \"熊\"}):\n",
    "    print(s.content, end=\"\", flush=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "659f95bf-5f3b-4126-b20a-55c2e493d704",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='当然可以！给你讲一个关于熊的冷笑话：\\n\\n---\\n\\n有一天，一只熊走进了一家药店，问药剂师：“你们有蜂蜜吗？”\\n\\n药剂师说：“没有，我们不卖蜂蜜。”\\n\\n第二天，这只熊又来了，问：“你们有蜂蜜吗？”\\n\\n药剂师叹了口气：“我说过我们不卖蜂蜜。”\\n\\n第三天，熊又来了，问：“你们有蜂蜜吗？”\\n\\n药剂师有点生气了：“我们从来没有蜂蜜！你为什么一直来问？”\\n\\n熊耸耸肩：“我只是想确认一下……你们有没有蜂蜜。”\\n\\n药剂师很无语：“那你到底要不要买别的东西？”\\n\\n熊想了想，说：“那……你们有创可贴吗？”\\n\\n药剂师问：“你要创可贴干什么？”\\n\\n熊小声说：“我刚才在蜂蜜罐子上摔了一跤……”\\n\\n---\\n\\n是不是有点冷？😄 你还想听更多关于熊的故事或笑话吗？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 196, 'prompt_tokens': 19, 'total_tokens': 215, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-a273afbf-2021-939f-a656-9e29d78673ef', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--a87612ed-7989-4fe4-a8a0-a9151105b92c-0', usage_metadata={'input_tokens': 19, 'output_tokens': 196, 'total_tokens': 215, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.invoke({\"topic\": \"熊\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "2d49ea2e-2dce-437f-96ab-ff22387e1d38",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='当然可以！这是一个关于熊的冷笑话：\\n\\n有一天，一只熊走进了一家药店，问药剂师：“你们有卖止痛药吗？”\\n\\n药剂师说：“有啊，怎么了？”\\n\\n熊说：“那给我来一瓶‘熊去氧胆酸’吧。”\\n\\n药剂师一愣：“这药是给熊去氧胆酸的？”\\n\\n熊叹了口气：“不然呢？难道是给你去氧胆酸的？”\\n\\n😄\\n\\n想听更多类似的动物笑话吗？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 105, 'prompt_tokens': 19, 'total_tokens': 124, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-20b20ffd-1a93-9c68-817e-ab72ed28a79c', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--2696c7ec-e11e-49e2-8a44-3239ddcbae30-0', usage_metadata={'input_tokens': 19, 'output_tokens': 105, 'total_tokens': 124, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       " AIMessage(content='当然！来一个轻松的猫猫笑话：\\n\\n有一天，一只猫走进了图书馆，走到前台对图书管理员说：\\n\\n“喵~ 有没有《鱼》这本书？”\\n\\n图书管理员一愣，给了它一本书。\\n\\n第二天，猫又来了：\\n\\n“喵~ 有没有《鱼》续集？”\\n\\n图书管理员笑了，又给了它另一本书。\\n\\n第三天，猫又来了：\\n\\n“喵~ 有没有《鱼》第三部？”\\n\\n图书管理员忍不住了，问：“你为什么总来看《鱼》这本书？”\\n\\n猫歪着头说：\\n\\n“喵？我是在查……有没有‘鱼’（余）力啊！”\\n\\n😄\\n\\n希望你喜欢这个“猫言猫语”笑话！要不要再来一个？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 150, 'prompt_tokens': 19, 'total_tokens': 169, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-648c7403-e279-9892-892a-3c29cb417f62', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--8c903775-9648-4424-bfe4-4f4841fd2731-0', usage_metadata={'input_tokens': 19, 'output_tokens': 150, 'total_tokens': 169, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain.batch([{\"topic\": \"熊\"}, {\"topic\": \"猫\"}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "9bf7d107-cf12-4b1c-a696-6f21c4543653",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='当然！给你讲一个关于熊的冷笑话：\\n\\n---\\n\\n有一天，一只熊走进了一家药店，问药剂师：“你们有蜂蜜吗？”\\n\\n药剂师说：“没有，我们这里是药店，不是超市。”\\n\\n第二天，熊又来了：“你们有蜂蜜吗？”\\n\\n药剂师有点不耐烦：“我不是说了吗？我们不卖蜂蜜！”\\n\\n第三天，熊又来了：“你们有蜂蜜吗？”\\n\\n药剂师生气了：“你是不是记不住？我们不卖蜂蜜！你再问我就把你赶出去！”\\n\\n第四天，熊又来了，药剂师一看见它就吼：“你再说‘蜂蜜’两个字，我就报警！”\\n\\n熊小心翼翼地说：“那……你们有创可贴吗？”\\n\\n药剂师愣了一下：“有啊，怎么了？”\\n\\n熊说：“我摔了一跤，膝盖破了。”\\n\\n药剂师问：“那你刚才为什么不直接问创可贴？”\\n\\n熊小声说：“我本来是想问蜂蜜的……我怕你又熊我。”\\n\\n---\\n\\n😄 希望你笑了！要不要再来一个？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 229, 'prompt_tokens': 19, 'total_tokens': 248, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-60fc73df-17ae-9d9d-96db-96590703bea7', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--0a3a7ec9-25af-441f-b0fb-050ecf80d08e-0', usage_metadata={'input_tokens': 19, 'output_tokens': 229, 'total_tokens': 248, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       " AIMessage(content='当然！这里有一个关于猫的冷幽默笑话：\\n\\n---\\n\\n有一天，一只猫走进了图书馆，走到前台对图书管理员说：\\n\\n“喵~”\\n\\n图书管理员给了它一本书。\\n\\n第二天，猫又来了，说：\\n\\n“喵~喵~”\\n\\n图书管理员又给了它一本书。\\n\\n第三天，猫又来了，说：\\n\\n“喵喵喵喵喵~”\\n\\n图书管理员叹了口气，说：“你能不能说点别的？”\\n\\n猫歪着头看着他说：\\n\\n“汪。”\\n\\n---\\n\\n希望你笑了😄 要不要再来一个？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 110, 'prompt_tokens': 19, 'total_tokens': 129, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-dec9e3d8-8b63-9799-b1a2-5f534a4896c6', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--6a1a1b23-71b0-4ba7-8bdb-7e903ca47a94-0', usage_metadata={'input_tokens': 19, 'output_tokens': 110, 'total_tokens': 129, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       " AIMessage(content='当然！给你讲一个关于狗的冷笑话，轻松一下：\\n\\n有一天，一只狗走进了图书馆，走到前台对图书管理员“汪”了一声。\\n\\n图书管理员一头雾水，但还是给了它一本书。\\n\\n狗看完后还了书，又“汪”了一声。\\n\\n图书管理员想了想，决定跟着它看看怎么回事。\\n\\n结果狗走到一个喷水池前，把书还了回去，说：“汪（我）不要了。”\\n\\n😂\\n\\n是不是很冷？不过狗狗真的很可爱对不对～还有更多笑话可以随时找我要哦！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 113, 'prompt_tokens': 19, 'total_tokens': 132, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-88a4e39c-b10e-9efe-a843-740de7eb98e9', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--2ec70b5e-f142-45e8-bc7d-d0ba33025304-0', usage_metadata={'input_tokens': 19, 'output_tokens': 113, 'total_tokens': 132, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#max_concurrency控制并发数\n",
    "chain.batch([{\"topic\": \"熊\"}, {\"topic\": \"猫\"}, {\"topic\": \"狗\"}], config={\"max_concurrency\": 5})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "1f99d6b2-fdae-4180-ad0a-e822387201a8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当然可以，这里有一个轻松幽默的笑话：\n",
      "\n",
      "---\n",
      "\n",
      "有一天，一位女士去超市买东西，走到货架前发现一瓶标价100元的香水。她皱了皱眉，自言自语道：“这价格也太贵了吧！”\n",
      "\n",
      "旁边一个男顾客听到了，热心地说：“女士，您别担心，这瓶香水我买给您！”\n",
      "\n",
      "女士转头看了他一眼，笑着说：“谢谢你啊，不过我不是舍不得买，我只是在想——男人为什么总比女人舍得花钱在香水上？”\n",
      "\n",
      "男顾客一愣，问：“什么意思？”\n",
      "\n",
      "女士淡定地答：“因为你们闻不到自己身上的味道啊！”\n",
      "\n",
      "---\n",
      "\n",
      "希望这个笑话能让你会心一笑 😄 有更多类型的笑话也欢迎告诉我！"
     ]
    }
   ],
   "source": [
    "# Async Stream 异步\n",
    "async for s in chain.astream({\"topic\": \"女人\"}):\n",
    "    print(s.content, end=\"\", flush=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "b1696476-fec9-48c8-9064-2b4d4c29bd65",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='当然可以，这里有一个轻松幽默的笑话：\\n\\n---\\n\\n有一天，一个男人去超市买鸡蛋。  \\n他走到货架前，拿起一盒鸡蛋，仔细看了看，然后突然皱起眉头，对旁边的售货员说：\\n\\n“这盒鸡蛋怎么没有生产日期？”\\n\\n售货员一脸淡定地回答：\\n\\n“先生，鸡生蛋的时候，是不会告诉你具体时间的。”\\n\\n---\\n\\n希望这个笑话让你笑了 😄 还有更多笑话随时来找我要！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 96, 'prompt_tokens': 19, 'total_tokens': 115, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-0ea9be62-0b1b-9463-a9aa-17271ab5b07e', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--858d92a8-5a19-4935-a195-874a9c973f9c-0', usage_metadata={'input_tokens': 19, 'output_tokens': 96, 'total_tokens': 115, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Async Invoke\n",
    "await chain.ainvoke({\"topic\": \"男人\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "56396af6-8e7e-4ab9-a16a-261ea3f17064",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='当然可以！给你讲一个关于熊的冷笑话：\\n\\n---\\n\\n有一天，一只熊走进了一家药店，问药剂师：“你们有治蜜蜂蜇的药膏吗？”\\n\\n药剂师说：“没有，我们这不卖那种药。”\\n\\n第二天，这只熊又来了，问：“你们有治蜜蜂蜇的药膏吗？”\\n\\n药剂师有点不耐烦地说：“我昨天就告诉过你了，我们不卖！”\\n\\n第三天，熊又来了，还是问同样的问题。\\n\\n药剂师终于忍不住了，说：“你为什么总是来问这个问题？！我们从来没有卖过治蜜蜂蜇的药膏！”\\n\\n熊挠挠头，说：“哦……我可能记错了……我是蜜蜂过敏。”\\n\\n---\\n\\n是不是有点冷？😄 但这就是熊的幽默！🐻', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 167, 'prompt_tokens': 19, 'total_tokens': 186, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-43414b83-da05-9ed6-88a5-8954efd07854', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--4561de42-62dd-4644-87e7-1da1f3a51353-0', usage_metadata={'input_tokens': 19, 'output_tokens': 167, 'total_tokens': 186, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       " AIMessage(content='当然可以，这里有一个轻松幽默的笑话：\\n\\n---\\n\\n有一天，一位女士去参加一个面试。  \\n面试官问她：“你最大的优点是什么？”  \\n女士想了想，回答道：“我特别擅长预测未来。”  \\n面试官很感兴趣：“哦？那你能举个例子吗？”  \\n她说：“比如，我知道你一定会问我有没有缺点。”  \\n面试官笑了：“那你的缺点是什么？”  \\n她淡定地说：“我最大的缺点就是——太爱说实话。”\\n\\n---\\n\\n希望这个笑话让你笑了 😄  \\n如果想要不同风格的笑话，也可以告诉我！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 114, 'prompt_tokens': 19, 'total_tokens': 133, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-8d6ad947-e64b-9dd5-9764-1a5479b343f5', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--cbe98ae5-0b60-4df0-bc9c-af9921f1658b-0', usage_metadata={'input_tokens': 19, 'output_tokens': 114, 'total_tokens': 133, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Async Batch\n",
    "await chain.abatch([{\"topic\": \"熊\"},{\"topic\": \"女人\"}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "dd0ba01c-056f-40f0-92f0-260d4d204109",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------\n",
      "RunLogPatch({'op': 'replace',\n",
      "  'path': '',\n",
      "  'value': {'final_output': None,\n",
      "            'id': '1dd848e5-c145-4657-a6e4-a9ebe363211c',\n",
      "            'logs': {},\n",
      "            'name': 'RunnableSequence',\n",
      "            'streamed_output': [],\n",
      "            'type': 'chain'}})\n",
      "----------------------------------------\n",
      "RunLogPatch({'op': 'add',\n",
      "  'path': '/logs/Docs',\n",
      "  'value': {'end_time': None,\n",
      "            'final_output': None,\n",
      "            'id': '956ad0d9-d5dc-4143-b0c3-5457907bdd4c',\n",
      "            'metadata': {'ls_embedding_provider': 'DashScopeEmbeddings',\n",
      "                         'ls_retriever_name': 'vectorstore',\n",
      "                         'ls_vector_store_provider': 'FAISS'},\n",
      "            'name': 'Docs',\n",
      "            'start_time': '2025-08-25T00:36:42.783+00:00',\n",
      "            'streamed_output': [],\n",
      "            'streamed_output_str': [],\n",
      "            'tags': ['map:key:context', 'FAISS', 'DashScopeEmbeddings'],\n",
      "            'type': 'retriever'}})\n",
      "----------------------------------------\n",
      "RunLogPatch({'op': 'add',\n",
      "  'path': '/logs/Docs/final_output',\n",
      "  'value': {'documents': [Document(id='80723256-4e68-455f-8f0b-4c18066fa279', metadata={}, page_content='柯基犬是一种中型家庭宠物犬')]}},\n",
      " {'op': 'add',\n",
      "  'path': '/logs/Docs/end_time',\n",
      "  'value': '2025-08-25T00:36:43.129+00:00'})\n",
      "----------------------------------------\n",
      "RunLogPatch({'op': 'add', 'path': '/streamed_output/-', 'value': ''},\n",
      " {'op': 'replace', 'path': '/final_output', 'value': ''})\n",
      "----------------------------------------\n",
      "RunLogPatch({'op': 'add', 'path': '/streamed_output/-', 'value': '柯'},\n",
      " {'op': 'replace', 'path': '/final_output', 'value': '柯'})\n",
      "----------------------------------------\n",
      "RunLogPatch({'op': 'add', 'path': '/streamed_output/-', 'value': '基是一种'},\n",
      " {'op': 'replace', 'path': '/final_output', 'value': '柯基是一种'})\n",
      "----------------------------------------\n",
      "RunLogPatch({'op': 'add', 'path': '/streamed_output/-', 'value': '中型家庭'},\n",
      " {'op': 'replace', 'path': '/final_output', 'value': '柯基是一种中型家庭'})\n",
      "----------------------------------------\n",
      "RunLogPatch({'op': 'add', 'path': '/streamed_output/-', 'value': '宠物犬。'},\n",
      " {'op': 'replace', 'path': '/final_output', 'value': '柯基是一种中型家庭宠物犬。'})\n",
      "----------------------------------------\n",
      "RunLogPatch({'op': 'add', 'path': '/streamed_output/-', 'value': ''})\n"
     ]
    }
   ],
   "source": [
    "# 异步获取中间步骤\n",
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain_community.embeddings import DashScopeEmbeddings  # 使用阿里云的嵌入模型\n",
    "from langchain_openai import ChatOpenAI\n",
    "\n",
    "model = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key = \"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base = \"https://dashscope.aliyuncs.com/compatible-mode/v1\"\n",
    "\n",
    ")\n",
    "\n",
    "template = \"\"\"基于下面的上下文来回答问题:\n",
    "{context}\n",
    "\n",
    "Question: {question}\n",
    "\"\"\"\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "# 使用阿里云的嵌入模型\n",
    "embeddings = DashScopeEmbeddings(\n",
    "    model=\"text-embedding-v1\",  # 阿里云的嵌入模型\n",
    "    dashscope_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\"\n",
    ")\n",
    "\n",
    "vectorstore = FAISS.from_texts(\n",
    "    [\"柯基犬是一种中型家庭宠物犬\"], embedding=embeddings\n",
    ")\n",
    "retriever = vectorstore.as_retriever()\n",
    "\n",
    "retrieval_chain = (\n",
    "    {\n",
    "        \"context\": retriever.with_config(run_name=\"Docs\"),\n",
    "        \"question\": RunnablePassthrough(),\n",
    "    }\n",
    "    | prompt\n",
    "    | model\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "async for chunk in retrieval_chain.astream_log(\n",
    "    \"柯基是什么?\", include_names=[\"Docs\"]\n",
    "):\n",
    "    print(\"-\" * 40)\n",
    "    print(chunk)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "26c3b9ad-904c-46e6-893f-bb4e9d346ff1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------------------------------------\n",
      "RunLog({'final_output': None,\n",
      " 'id': '4dcb14be-a64b-4965-82ed-3a1336635687',\n",
      " 'logs': {},\n",
      " 'name': 'RunnableSequence',\n",
      " 'streamed_output': [],\n",
      " 'type': 'chain'})\n",
      "----------------------------------------------------------------------\n",
      "RunLog({'final_output': None,\n",
      " 'id': '4dcb14be-a64b-4965-82ed-3a1336635687',\n",
      " 'logs': {'Docs': {'end_time': None,\n",
      "                   'final_output': None,\n",
      "                   'id': 'ae9561ba-140a-4f7a-9489-0b393c1da928',\n",
      "                   'metadata': {'ls_embedding_provider': 'DashScopeEmbeddings',\n",
      "                                'ls_retriever_name': 'vectorstore',\n",
      "                                'ls_vector_store_provider': 'FAISS'},\n",
      "                   'name': 'Docs',\n",
      "                   'start_time': '2025-08-25T00:38:42.976+00:00',\n",
      "                   'streamed_output': [],\n",
      "                   'streamed_output_str': [],\n",
      "                   'tags': ['map:key:context', 'FAISS', 'DashScopeEmbeddings'],\n",
      "                   'type': 'retriever'}},\n",
      " 'name': 'RunnableSequence',\n",
      " 'streamed_output': [],\n",
      " 'type': 'chain'})\n",
      "----------------------------------------------------------------------\n",
      "RunLog({'final_output': None,\n",
      " 'id': '4dcb14be-a64b-4965-82ed-3a1336635687',\n",
      " 'logs': {'Docs': {'end_time': '2025-08-25T00:38:43.257+00:00',\n",
      "                   'final_output': {'documents': [Document(id='80723256-4e68-455f-8f0b-4c18066fa279', metadata={}, page_content='柯基犬是一种中型家庭宠物犬')]},\n",
      "                   'id': 'ae9561ba-140a-4f7a-9489-0b393c1da928',\n",
      "                   'metadata': {'ls_embedding_provider': 'DashScopeEmbeddings',\n",
      "                                'ls_retriever_name': 'vectorstore',\n",
      "                                'ls_vector_store_provider': 'FAISS'},\n",
      "                   'name': 'Docs',\n",
      "                   'start_time': '2025-08-25T00:38:42.976+00:00',\n",
      "                   'streamed_output': [],\n",
      "                   'streamed_output_str': [],\n",
      "                   'tags': ['map:key:context', 'FAISS', 'DashScopeEmbeddings'],\n",
      "                   'type': 'retriever'}},\n",
      " 'name': 'RunnableSequence',\n",
      " 'streamed_output': [],\n",
      " 'type': 'chain'})\n",
      "----------------------------------------------------------------------\n",
      "RunLog({'final_output': '',\n",
      " 'id': '4dcb14be-a64b-4965-82ed-3a1336635687',\n",
      " 'logs': {'Docs': {'end_time': '2025-08-25T00:38:43.257+00:00',\n",
      "                   'final_output': {'documents': [Document(id='80723256-4e68-455f-8f0b-4c18066fa279', metadata={}, page_content='柯基犬是一种中型家庭宠物犬')]},\n",
      "                   'id': 'ae9561ba-140a-4f7a-9489-0b393c1da928',\n",
      "                   'metadata': {'ls_embedding_provider': 'DashScopeEmbeddings',\n",
      "                                'ls_retriever_name': 'vectorstore',\n",
      "                                'ls_vector_store_provider': 'FAISS'},\n",
      "                   'name': 'Docs',\n",
      "                   'start_time': '2025-08-25T00:38:42.976+00:00',\n",
      "                   'streamed_output': [],\n",
      "                   'streamed_output_str': [],\n",
      "                   'tags': ['map:key:context', 'FAISS', 'DashScopeEmbeddings'],\n",
      "                   'type': 'retriever'}},\n",
      " 'name': 'RunnableSequence',\n",
      " 'streamed_output': [''],\n",
      " 'type': 'chain'})\n",
      "----------------------------------------------------------------------\n",
      "RunLog({'final_output': '柯',\n",
      " 'id': '4dcb14be-a64b-4965-82ed-3a1336635687',\n",
      " 'logs': {'Docs': {'end_time': '2025-08-25T00:38:43.257+00:00',\n",
      "                   'final_output': {'documents': [Document(id='80723256-4e68-455f-8f0b-4c18066fa279', metadata={}, page_content='柯基犬是一种中型家庭宠物犬')]},\n",
      "                   'id': 'ae9561ba-140a-4f7a-9489-0b393c1da928',\n",
      "                   'metadata': {'ls_embedding_provider': 'DashScopeEmbeddings',\n",
      "                                'ls_retriever_name': 'vectorstore',\n",
      "                                'ls_vector_store_provider': 'FAISS'},\n",
      "                   'name': 'Docs',\n",
      "                   'start_time': '2025-08-25T00:38:42.976+00:00',\n",
      "                   'streamed_output': [],\n",
      "                   'streamed_output_str': [],\n",
      "                   'tags': ['map:key:context', 'FAISS', 'DashScopeEmbeddings'],\n",
      "                   'type': 'retriever'}},\n",
      " 'name': 'RunnableSequence',\n",
      " 'streamed_output': ['', '柯'],\n",
      " 'type': 'chain'})\n",
      "----------------------------------------------------------------------\n",
      "RunLog({'final_output': '柯基是一种',\n",
      " 'id': '4dcb14be-a64b-4965-82ed-3a1336635687',\n",
      " 'logs': {'Docs': {'end_time': '2025-08-25T00:38:43.257+00:00',\n",
      "                   'final_output': {'documents': [Document(id='80723256-4e68-455f-8f0b-4c18066fa279', metadata={}, page_content='柯基犬是一种中型家庭宠物犬')]},\n",
      "                   'id': 'ae9561ba-140a-4f7a-9489-0b393c1da928',\n",
      "                   'metadata': {'ls_embedding_provider': 'DashScopeEmbeddings',\n",
      "                                'ls_retriever_name': 'vectorstore',\n",
      "                                'ls_vector_store_provider': 'FAISS'},\n",
      "                   'name': 'Docs',\n",
      "                   'start_time': '2025-08-25T00:38:42.976+00:00',\n",
      "                   'streamed_output': [],\n",
      "                   'streamed_output_str': [],\n",
      "                   'tags': ['map:key:context', 'FAISS', 'DashScopeEmbeddings'],\n",
      "                   'type': 'retriever'}},\n",
      " 'name': 'RunnableSequence',\n",
      " 'streamed_output': ['', '柯', '基是一种'],\n",
      " 'type': 'chain'})\n",
      "----------------------------------------------------------------------\n",
      "RunLog({'final_output': '柯基是一种中型家庭',\n",
      " 'id': '4dcb14be-a64b-4965-82ed-3a1336635687',\n",
      " 'logs': {'Docs': {'end_time': '2025-08-25T00:38:43.257+00:00',\n",
      "                   'final_output': {'documents': [Document(id='80723256-4e68-455f-8f0b-4c18066fa279', metadata={}, page_content='柯基犬是一种中型家庭宠物犬')]},\n",
      "                   'id': 'ae9561ba-140a-4f7a-9489-0b393c1da928',\n",
      "                   'metadata': {'ls_embedding_provider': 'DashScopeEmbeddings',\n",
      "                                'ls_retriever_name': 'vectorstore',\n",
      "                                'ls_vector_store_provider': 'FAISS'},\n",
      "                   'name': 'Docs',\n",
      "                   'start_time': '2025-08-25T00:38:42.976+00:00',\n",
      "                   'streamed_output': [],\n",
      "                   'streamed_output_str': [],\n",
      "                   'tags': ['map:key:context', 'FAISS', 'DashScopeEmbeddings'],\n",
      "                   'type': 'retriever'}},\n",
      " 'name': 'RunnableSequence',\n",
      " 'streamed_output': ['', '柯', '基是一种', '中型家庭'],\n",
      " 'type': 'chain'})\n",
      "----------------------------------------------------------------------\n",
      "RunLog({'final_output': '柯基是一种中型家庭宠物犬。',\n",
      " 'id': '4dcb14be-a64b-4965-82ed-3a1336635687',\n",
      " 'logs': {'Docs': {'end_time': '2025-08-25T00:38:43.257+00:00',\n",
      "                   'final_output': {'documents': [Document(id='80723256-4e68-455f-8f0b-4c18066fa279', metadata={}, page_content='柯基犬是一种中型家庭宠物犬')]},\n",
      "                   'id': 'ae9561ba-140a-4f7a-9489-0b393c1da928',\n",
      "                   'metadata': {'ls_embedding_provider': 'DashScopeEmbeddings',\n",
      "                                'ls_retriever_name': 'vectorstore',\n",
      "                                'ls_vector_store_provider': 'FAISS'},\n",
      "                   'name': 'Docs',\n",
      "                   'start_time': '2025-08-25T00:38:42.976+00:00',\n",
      "                   'streamed_output': [],\n",
      "                   'streamed_output_str': [],\n",
      "                   'tags': ['map:key:context', 'FAISS', 'DashScopeEmbeddings'],\n",
      "                   'type': 'retriever'}},\n",
      " 'name': 'RunnableSequence',\n",
      " 'streamed_output': ['', '柯', '基是一种', '中型家庭', '宠物犬。'],\n",
      " 'type': 'chain'})\n",
      "----------------------------------------------------------------------\n",
      "RunLog({'final_output': '柯基是一种中型家庭宠物犬。',\n",
      " 'id': '4dcb14be-a64b-4965-82ed-3a1336635687',\n",
      " 'logs': {'Docs': {'end_time': '2025-08-25T00:38:43.257+00:00',\n",
      "                   'final_output': {'documents': [Document(id='80723256-4e68-455f-8f0b-4c18066fa279', metadata={}, page_content='柯基犬是一种中型家庭宠物犬')]},\n",
      "                   'id': 'ae9561ba-140a-4f7a-9489-0b393c1da928',\n",
      "                   'metadata': {'ls_embedding_provider': 'DashScopeEmbeddings',\n",
      "                                'ls_retriever_name': 'vectorstore',\n",
      "                                'ls_vector_store_provider': 'FAISS'},\n",
      "                   'name': 'Docs',\n",
      "                   'start_time': '2025-08-25T00:38:42.976+00:00',\n",
      "                   'streamed_output': [],\n",
      "                   'streamed_output_str': [],\n",
      "                   'tags': ['map:key:context', 'FAISS', 'DashScopeEmbeddings'],\n",
      "                   'type': 'retriever'}},\n",
      " 'name': 'RunnableSequence',\n",
      " 'streamed_output': ['', '柯', '基是一种', '中型家庭', '宠物犬。', ''],\n",
      " 'type': 'chain'})\n"
     ]
    }
   ],
   "source": [
    "# 只看状态值\n",
    "async for chunk in retrieval_chain.astream_log(\n",
    "    \"柯基是什么?\", include_names=[\"Docs\"], diff=False\n",
    "):\n",
    "    print(\"-\" * 70)\n",
    "    print(chunk)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "4422486a-0ff4-4faf-b948-c03d9b4508ca",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='当然可以！这是一个关于熊的冷笑话：\\n\\n---\\n\\n有一天，一只熊走进了一家药店，问药剂师：“你们有蜂蜜吗？”\\n\\n药剂师说：“没有，我们不卖蜂蜜。”\\n\\n第二天，这只熊又来了，问：“你们有蜂蜜吗？”\\n\\n药剂师叹了口气：“我说过我们不卖蜂蜜，别再问了。”\\n\\n第三天，熊又来了，继续问：“你们有蜂蜜吗？”\\n\\n药剂师有点生气了：“我不是告诉你了没有蜂蜜吗！再问我就把你赶出去！”\\n\\n第四天，熊又来了，药剂师怒吼：“你要是再问有没有蜂蜜，我就把你关起来！”\\n\\n熊想了想，小心翼翼地问：“那……你们有创可贴吗？”\\n\\n药剂师愣了一下：“有啊，干嘛？”\\n\\n熊小声说：“那好吧……我屁股被你们家蜂蜜粘住了。”\\n\\n---\\n\\n😄 希望这个笑话让你笑一下！要不要再来一个？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 201, 'prompt_tokens': 19, 'total_tokens': 220, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-dd0fc50b-92f8-9979-b367-e0ac3db46017', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--a38dad59-342d-48cf-9f8c-2d7d9d90fd33-0', usage_metadata={'input_tokens': 19, 'output_tokens': 201, 'total_tokens': 220, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 并行支持\n",
    "from langchain_core.runnables import RunnableParallel\n",
    "\n",
    "chain1 = ChatPromptTemplate.from_template(\"给我讲一个关于{topic}的笑话\") | model\n",
    "chain2 = (\n",
    "    ChatPromptTemplate.from_template(\"写两行关于{topic}的诗歌\")\n",
    "    | model\n",
    ")\n",
    "combined = RunnableParallel(joke=chain1, poem=chain2)\n",
    "\n",
    "chain1.invoke({\"topic\": \"熊\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "e2cb3768-9994-4cfe-a90c-11f385de644a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='林间巨影踏青苔，  \\n月下独行带松风。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 16, 'prompt_tokens': 19, 'total_tokens': 35, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-39ad5740-31d6-99a3-939d-728cb5a34d08', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--1e10ab55-1eec-4fd8-be8c-8f7221fe9b76-0', usage_metadata={'input_tokens': 19, 'output_tokens': 16, 'total_tokens': 35, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain2.invoke({\"topic\": \"熊\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "c6f9faf0-23e3-4366-9043-9a68d0b34d3e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'joke': AIMessage(content='当然可以！给你讲一个关于熊的冷笑话：\\n\\n---\\n\\n有一天，一只熊走进了一家药店，问药剂师：“你们有蜂蜜吗？”\\n\\n药剂师说：“没有，我们不卖蜂蜜。”\\n\\n第二天，这只熊又来了，问：“你们有蜂蜜吗？”\\n\\n药剂师有点奇怪，但还是回答：“没有啊，我们真的不卖蜂蜜。”\\n\\n第三天，熊又来了，问了同样的问题。\\n\\n药剂师终于忍不住了，说：“我们从来都不卖蜂蜜，你为什么每天都要来问？”\\n\\n熊挠挠头说：“我忘了……我有蜜蜂过敏，想买点蜂蜜吃，可我记性不好，只能每天来问一遍。”\\n\\n---\\n\\n是不是有点“熊”味儿了？😄 还想听一个吗？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 164, 'prompt_tokens': 19, 'total_tokens': 183, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-2049f5a9-48d1-9a61-927c-6f797543c885', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--ffe5283e-c7c2-4e53-96ad-f34c7a739058-0', usage_metadata={'input_tokens': 19, 'output_tokens': 164, 'total_tokens': 183, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       " 'poem': AIMessage(content='林间巨影踏新泥，  \\n风过寒松带熊迹。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 19, 'total_tokens': 36, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-c583bdd3-9115-9839-a46e-c1ae15814bac', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--a11872d9-f23f-4ffa-82a9-48bad29a84ae-0', usage_metadata={'input_tokens': 19, 'output_tokens': 17, 'total_tokens': 36, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})}"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 并行执行\n",
    "combined.invoke({\"topic\": \"熊\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "a33fbee4-29c9-426b-bdbd-d81cfa3c512e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='当然！给你讲一个关于熊的冷笑话：\\n\\n---\\n\\n有一天，一只熊走进了一家药店，问药剂师：“你们有卖润唇膏吗？”\\n\\n药剂师问：“你自己涂吗？”\\n\\n熊说：“不，我要去滑雪，风吹得我嘴巴好干。”\\n\\n药剂师又问：“那你滑雪用什么当雪橇？”\\n\\n熊回答：“用我的熊掌啊！”\\n\\n药剂师笑了：“熊掌？那你为什么不直接用‘滑’掌？”\\n\\n---\\n\\n是不是有点冷？😄 你还想听更多关于动物的笑话吗？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 121, 'prompt_tokens': 19, 'total_tokens': 140, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-69759a7b-f3a0-9682-b5f4-a3dbb0bcef16', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--afcb8d3c-efd6-423f-9bcc-86f557d699ba-0', usage_metadata={'input_tokens': 19, 'output_tokens': 121, 'total_tokens': 140, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       " AIMessage(content='当然！给你讲一个轻松可爱的猫猫笑话：\\n\\n---\\n\\n有一天，一只猫走进了图书馆，走到前台对图书管理员说：\\n\\n“喵~ 有没有关于鱼的书？”\\n\\n图书管理员一愣，给了它一本书。\\n\\n第二天，猫又来了：\\n\\n“喵~ 有没有关于鱼的书？”\\n\\n图书管理员又给了它同一本书。\\n\\n第三天，猫又来了：\\n\\n“喵~ 有没有关于鱼的书？”\\n\\n这次，图书管理员有点疑惑，但还是又给了它那本书。\\n\\n第四天，猫又来了……\\n\\n图书管理员终于忍不住了，翻开书，在第一页写上：“别看了，赶紧去抓老鼠！”\\n\\n第五天，猫又来了：\\n\\n“喵~ 有没有关于鱼的DVD？”\\n\\n---\\n\\n是不是很猫？😺  \\n要再来一个吗？', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 165, 'prompt_tokens': 19, 'total_tokens': 184, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-e8e31052-a6e1-9240-8868-081fa5b128d6', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--95351116-e547-431d-99e3-6af751438cd3-0', usage_metadata={'input_tokens': 19, 'output_tokens': 165, 'total_tokens': 184, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 并行批处理，适用于大量生成\n",
    "chain1.batch([{\"topic\": \"熊\"}, {\"topic\": \"猫\"}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "d6dcade0-5028-4da2-8ebc-75fb1c3dd7eb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[AIMessage(content='林间巨影踏春泥，  \\n琥珀双眸映晨曦。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 19, 'total_tokens': 36, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-5f6b9c5a-6ee9-98ab-bb3a-2d3356298a28', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--835caa6b-3826-4229-8f78-1f35404f496d-0', usage_metadata={'input_tokens': 19, 'output_tokens': 17, 'total_tokens': 36, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       " AIMessage(content='1. 轻步踏月影，  \\n2. 独眠花下风。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 21, 'prompt_tokens': 19, 'total_tokens': 40, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-3115f8a0-f2d4-9490-a668-40427abc29b7', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--43cc7786-e345-4512-9a2a-cda130d17b92-0', usage_metadata={'input_tokens': 19, 'output_tokens': 21, 'total_tokens': 40, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})]"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chain2.batch([{\"topic\": \"熊\"}, {\"topic\": \"猫\"}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "bb8ea5a7-b151-4737-80d5-db83f2a80272",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'joke': AIMessage(content='当然可以！给你讲一个关于熊的冷笑话：\\n\\n---\\n\\n有一天，一只熊走进了一家药店，对药剂师说：\\n\\n“你们有没有蜂蜜药膏？”\\n\\n药剂师一愣，说：“这里是药店，不是蜂蜜店，你是不是走错了？”\\n\\n第二天，这只熊又来了，问：“你们有没有蜂蜜药膏？”\\n\\n药剂师有点生气了：“我不是告诉过你了吗，这里是药店，没有蜂蜜卖！”\\n\\n第三天，熊又来了，还是问：“你们有没有蜂蜜药膏？”\\n\\n药剂师终于忍不住了，大吼：“你再问一次，我就把你抓起来！”\\n\\n熊一脸无辜地说：“我只是想确认一下……你有没有‘熊’心病？”\\n\\n---\\n\\n😄 希望你喜欢这个笑话！想听更多可以随时告诉我！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 168, 'prompt_tokens': 19, 'total_tokens': 187, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-e76e83ec-5003-90ae-9307-9c866c600660', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--54b04ee9-a2c3-4b67-bf01-1b2557caa889-0', usage_metadata={'input_tokens': 19, 'output_tokens': 168, 'total_tokens': 187, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       "  'poem': AIMessage(content='林间巨影踏秋霜，  \\n琥珀双眸映斜阳。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 19, 'total_tokens': 36, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-4d1bd112-21ff-9747-b96b-9a5c56555648', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--5848b3a9-89f2-4b3a-a321-56fbae1110ae-0', usage_metadata={'input_tokens': 19, 'output_tokens': 17, 'total_tokens': 36, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})},\n",
       " {'joke': AIMessage(content='当然！这里有一个轻松可爱的关于猫的笑话：\\n\\n---\\n\\n有一天，一只猫走进了图书馆，走到前台对图书管理员说：\\n\\n“喵~ 有没有鱼的书？”\\n\\n图书管理员一愣，给了它一本关于鱼的书。\\n\\n第二天，猫又来了，说：\\n\\n“喵~ 有没有鱼的书？”\\n\\n图书管理员只好又给它一本不同的关于鱼的书。\\n\\n第三天，猫又来了，还是说：\\n\\n“喵~ 有没有鱼的书？”\\n\\n图书管理员叹了口气，决定今天跟着猫看看它到底在干什么。\\n\\n只见猫出了图书馆，跑到公园里，把书放在地上，对着书大叫：\\n\\n“喵！！我不是说了没有‘鼠’的书吗！！！”\\n\\n---\\n\\n希望这个笑话让你笑了 😸 想听更多关于猫的趣事也可以告诉我！', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 170, 'prompt_tokens': 19, 'total_tokens': 189, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-b4de98fa-dd54-981b-b878-5f6cc0664001', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--494a5f06-a457-4f9b-b060-fdc1c160e9ca-0', usage_metadata={'input_tokens': 19, 'output_tokens': 170, 'total_tokens': 189, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}),\n",
       "  'poem': AIMessage(content='月影轻摇窗，猫儿踏梦行。  \\n绒尾拂星河，悄语入深更。', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 25, 'prompt_tokens': 19, 'total_tokens': 44, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}}, 'model_name': 'qwen-plus', 'system_fingerprint': None, 'id': 'chatcmpl-7f55a244-3c30-9b47-b8e1-4b3a468bfd52', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None}, id='run--fc0ef026-2789-4e31-b62c-b43b21c3a1ea-0', usage_metadata={'input_tokens': 19, 'output_tokens': 25, 'total_tokens': 44, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}})}]"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 并行执行\n",
    "combined.batch([{\"topic\": \"熊\"}, {\"topic\": \"猫\"}])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3822589d-365c-48ad-8837-8d2329a1b7f2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
      "\u001b[32;1m\u001b[1;3m我需要查找现任美国总统的名字以及他的年龄，然后计算他的年龄的平方。\n",
      "Action: duckduckgo_results_json\n",
      "Action Input: 现任美国总统是谁？他的年龄是多少？\u001b[0m"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "D:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_community\\utilities\\duckduckgo_search.py:63: RuntimeWarning: This package (`duckduckgo_search`) has been renamed to `ddgs`! Use `pip install ddgs` instead.\n",
      "  with DDGS() as ddgs:\n"
     ]
    },
    {
     "ename": "DuckDuckGoSearchException",
     "evalue": "https://www.bing.com/search?q=%E7%8E%B0%E4%BB%BB%E7%BE%8E%E5%9B%BD%E6%80%BB%E7%BB%9F%E6%98%AF%E8%B0%81%EF%BC%9F%E4%BB%96%E7%9A%84%E5%B9%B4%E9%BE%84%E6%98%AF%E5%A4%9A%E5%B0%91%EF%BC%9F&filters=ex1%3A%22ez5_19959_20324%22 return None. params={'q': '现任美国总统是谁？他的年龄是多少？', 'filters': 'ex1:\"ez5_19959_20324\"'} content=None data=None",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mDuckDuckGoSearchException\u001b[0m                 Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[2], line 36\u001b[0m\n\u001b[0;32m     27\u001b[0m tools\u001b[38;5;241m.\u001b[39mappend(search_tool)\n\u001b[0;32m     29\u001b[0m agent \u001b[38;5;241m=\u001b[39m initialize_agent(\n\u001b[0;32m     30\u001b[0m     tools,\n\u001b[0;32m     31\u001b[0m     llm,\n\u001b[0;32m     32\u001b[0m     agent\u001b[38;5;241m=\u001b[39mAgentType\u001b[38;5;241m.\u001b[39mZERO_SHOT_REACT_DESCRIPTION,\n\u001b[0;32m     33\u001b[0m     verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[0;32m     34\u001b[0m )\n\u001b[1;32m---> 36\u001b[0m \u001b[43magent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m请问现任的美国总统是谁？他的年龄的平方是多少?\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m     38\u001b[0m \u001b[38;5;66;03m# 上面的代码在华为云主机上执行正常！\u001b[39;00m\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain\\chains\\base.py:167\u001b[0m, in \u001b[0;36mChain.invoke\u001b[1;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[0;32m    165\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m    166\u001b[0m     run_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e)\n\u001b[1;32m--> 167\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m e\n\u001b[0;32m    168\u001b[0m run_manager\u001b[38;5;241m.\u001b[39mon_chain_end(outputs)\n\u001b[0;32m    170\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m include_run_info:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain\\chains\\base.py:157\u001b[0m, in \u001b[0;36mChain.invoke\u001b[1;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[0;32m    154\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m    155\u001b[0m     \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_validate_inputs(inputs)\n\u001b[0;32m    156\u001b[0m     outputs \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m--> 157\u001b[0m         \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call\u001b[49m\u001b[43m(\u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    158\u001b[0m         \u001b[38;5;28;01mif\u001b[39;00m new_arg_supported\n\u001b[0;32m    159\u001b[0m         \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_call(inputs)\n\u001b[0;32m    160\u001b[0m     )\n\u001b[0;32m    162\u001b[0m     final_outputs: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, Any] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprep_outputs(\n\u001b[0;32m    163\u001b[0m         inputs, outputs, return_only_outputs\n\u001b[0;32m    164\u001b[0m     )\n\u001b[0;32m    165\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain\\agents\\agent.py:1620\u001b[0m, in \u001b[0;36mAgentExecutor._call\u001b[1;34m(self, inputs, run_manager)\u001b[0m\n\u001b[0;32m   1618\u001b[0m \u001b[38;5;66;03m# We now enter the agent loop (until it returns something).\u001b[39;00m\n\u001b[0;32m   1619\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_should_continue(iterations, time_elapsed):\n\u001b[1;32m-> 1620\u001b[0m     next_step_output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_take_next_step\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1621\u001b[0m \u001b[43m        \u001b[49m\u001b[43mname_to_tool_map\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1622\u001b[0m \u001b[43m        \u001b[49m\u001b[43mcolor_mapping\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1623\u001b[0m \u001b[43m        \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1624\u001b[0m \u001b[43m        \u001b[49m\u001b[43mintermediate_steps\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1625\u001b[0m \u001b[43m        \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1626\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1627\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(next_step_output, AgentFinish):\n\u001b[0;32m   1628\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_return(\n\u001b[0;32m   1629\u001b[0m             next_step_output, intermediate_steps, run_manager\u001b[38;5;241m=\u001b[39mrun_manager\n\u001b[0;32m   1630\u001b[0m         )\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain\\agents\\agent.py:1326\u001b[0m, in \u001b[0;36mAgentExecutor._take_next_step\u001b[1;34m(self, name_to_tool_map, color_mapping, inputs, intermediate_steps, run_manager)\u001b[0m\n\u001b[0;32m   1317\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21m_take_next_step\u001b[39m(\n\u001b[0;32m   1318\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m   1319\u001b[0m     name_to_tool_map: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, BaseTool],\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1323\u001b[0m     run_manager: Optional[CallbackManagerForChainRun] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m   1324\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Union[AgentFinish, \u001b[38;5;28mlist\u001b[39m[\u001b[38;5;28mtuple\u001b[39m[AgentAction, \u001b[38;5;28mstr\u001b[39m]]]:\n\u001b[0;32m   1325\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_consume_next_step(\n\u001b[1;32m-> 1326\u001b[0m         [\n\u001b[0;32m   1327\u001b[0m             a\n\u001b[0;32m   1328\u001b[0m             \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iter_next_step(\n\u001b[0;32m   1329\u001b[0m                 name_to_tool_map,\n\u001b[0;32m   1330\u001b[0m                 color_mapping,\n\u001b[0;32m   1331\u001b[0m                 inputs,\n\u001b[0;32m   1332\u001b[0m                 intermediate_steps,\n\u001b[0;32m   1333\u001b[0m                 run_manager,\n\u001b[0;32m   1334\u001b[0m             )\n\u001b[0;32m   1335\u001b[0m         ]\n\u001b[0;32m   1336\u001b[0m     )\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain\\agents\\agent.py:1326\u001b[0m, in \u001b[0;36m<listcomp>\u001b[1;34m(.0)\u001b[0m\n\u001b[0;32m   1317\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21m_take_next_step\u001b[39m(\n\u001b[0;32m   1318\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m   1319\u001b[0m     name_to_tool_map: \u001b[38;5;28mdict\u001b[39m[\u001b[38;5;28mstr\u001b[39m, BaseTool],\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1323\u001b[0m     run_manager: Optional[CallbackManagerForChainRun] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m   1324\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Union[AgentFinish, \u001b[38;5;28mlist\u001b[39m[\u001b[38;5;28mtuple\u001b[39m[AgentAction, \u001b[38;5;28mstr\u001b[39m]]]:\n\u001b[0;32m   1325\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_consume_next_step(\n\u001b[1;32m-> 1326\u001b[0m         [\n\u001b[0;32m   1327\u001b[0m             a\n\u001b[0;32m   1328\u001b[0m             \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iter_next_step(\n\u001b[0;32m   1329\u001b[0m                 name_to_tool_map,\n\u001b[0;32m   1330\u001b[0m                 color_mapping,\n\u001b[0;32m   1331\u001b[0m                 inputs,\n\u001b[0;32m   1332\u001b[0m                 intermediate_steps,\n\u001b[0;32m   1333\u001b[0m                 run_manager,\n\u001b[0;32m   1334\u001b[0m             )\n\u001b[0;32m   1335\u001b[0m         ]\n\u001b[0;32m   1336\u001b[0m     )\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain\\agents\\agent.py:1411\u001b[0m, in \u001b[0;36mAgentExecutor._iter_next_step\u001b[1;34m(self, name_to_tool_map, color_mapping, inputs, intermediate_steps, run_manager)\u001b[0m\n\u001b[0;32m   1409\u001b[0m     \u001b[38;5;28;01myield\u001b[39;00m agent_action\n\u001b[0;32m   1410\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m agent_action \u001b[38;5;129;01min\u001b[39;00m actions:\n\u001b[1;32m-> 1411\u001b[0m     \u001b[38;5;28;01myield\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_perform_agent_action\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m   1412\u001b[0m \u001b[43m        \u001b[49m\u001b[43mname_to_tool_map\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcolor_mapping\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43magent_action\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\n\u001b[0;32m   1413\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain\\agents\\agent.py:1433\u001b[0m, in \u001b[0;36mAgentExecutor._perform_agent_action\u001b[1;34m(self, name_to_tool_map, color_mapping, agent_action, run_manager)\u001b[0m\n\u001b[0;32m   1431\u001b[0m         tool_run_kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mllm_prefix\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m   1432\u001b[0m     \u001b[38;5;66;03m# We then call the tool on the tool input to get an observation\u001b[39;00m\n\u001b[1;32m-> 1433\u001b[0m     observation \u001b[38;5;241m=\u001b[39m tool\u001b[38;5;241m.\u001b[39mrun(\n\u001b[0;32m   1434\u001b[0m         agent_action\u001b[38;5;241m.\u001b[39mtool_input,\n\u001b[0;32m   1435\u001b[0m         verbose\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mverbose,\n\u001b[0;32m   1436\u001b[0m         color\u001b[38;5;241m=\u001b[39mcolor,\n\u001b[0;32m   1437\u001b[0m         callbacks\u001b[38;5;241m=\u001b[39mrun_manager\u001b[38;5;241m.\u001b[39mget_child() \u001b[38;5;28;01mif\u001b[39;00m run_manager \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m   1438\u001b[0m         \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mtool_run_kwargs,\n\u001b[0;32m   1439\u001b[0m     )\n\u001b[0;32m   1440\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m   1441\u001b[0m     tool_run_kwargs \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_action_agent\u001b[38;5;241m.\u001b[39mtool_run_logging_kwargs()\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\tools\\base.py:888\u001b[0m, in \u001b[0;36mBaseTool.run\u001b[1;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, tool_call_id, **kwargs)\u001b[0m\n\u001b[0;32m    886\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m error_to_raise:\n\u001b[0;32m    887\u001b[0m     run_manager\u001b[38;5;241m.\u001b[39mon_tool_error(error_to_raise)\n\u001b[1;32m--> 888\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m error_to_raise\n\u001b[0;32m    889\u001b[0m output \u001b[38;5;241m=\u001b[39m _format_output(content, artifact, tool_call_id, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname, status)\n\u001b[0;32m    890\u001b[0m run_manager\u001b[38;5;241m.\u001b[39mon_tool_end(output, color\u001b[38;5;241m=\u001b[39mcolor, name\u001b[38;5;241m=\u001b[39m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_core\\tools\\base.py:857\u001b[0m, in \u001b[0;36mBaseTool.run\u001b[1;34m(self, tool_input, verbose, start_color, color, callbacks, tags, metadata, run_name, run_id, config, tool_call_id, **kwargs)\u001b[0m\n\u001b[0;32m    855\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m config_param \u001b[38;5;241m:=\u001b[39m _get_runnable_config_param(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_run):\n\u001b[0;32m    856\u001b[0m         tool_kwargs \u001b[38;5;241m|\u001b[39m\u001b[38;5;241m=\u001b[39m {config_param: config}\n\u001b[1;32m--> 857\u001b[0m     response \u001b[38;5;241m=\u001b[39m context\u001b[38;5;241m.\u001b[39mrun(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_run, \u001b[38;5;241m*\u001b[39mtool_args, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mtool_kwargs)\n\u001b[0;32m    858\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mresponse_format \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcontent_and_artifact\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m    859\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(response, \u001b[38;5;28mtuple\u001b[39m) \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mlen\u001b[39m(response) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;241m2\u001b[39m:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_community\\tools\\ddg_search\\tool.py:112\u001b[0m, in \u001b[0;36mDuckDuckGoSearchResults._run\u001b[1;34m(self, query, run_manager)\u001b[0m\n\u001b[0;32m    106\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21m_run\u001b[39m(\n\u001b[0;32m    107\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m    108\u001b[0m     query: \u001b[38;5;28mstr\u001b[39m,\n\u001b[0;32m    109\u001b[0m     run_manager: Optional[CallbackManagerForToolRun] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[0;32m    110\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28mtuple\u001b[39m[Union[List[\u001b[38;5;28mdict\u001b[39m], \u001b[38;5;28mstr\u001b[39m], List[\u001b[38;5;28mdict\u001b[39m]]:\n\u001b[0;32m    111\u001b[0m \u001b[38;5;250m    \u001b[39m\u001b[38;5;124;03m\"\"\"Use the tool.\"\"\"\u001b[39;00m\n\u001b[1;32m--> 112\u001b[0m     raw_results \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mapi_wrapper\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresults\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m    113\u001b[0m \u001b[43m        \u001b[49m\u001b[43mquery\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmax_results\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msource\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackend\u001b[49m\n\u001b[0;32m    114\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    115\u001b[0m     results \u001b[38;5;241m=\u001b[39m [\n\u001b[0;32m    116\u001b[0m         {\n\u001b[0;32m    117\u001b[0m             k: v\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    121\u001b[0m         \u001b[38;5;28;01mfor\u001b[39;00m d \u001b[38;5;129;01min\u001b[39;00m raw_results\n\u001b[0;32m    122\u001b[0m     ]\n\u001b[0;32m    124\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39moutput_format \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlist\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_community\\utilities\\duckduckgo_search.py:146\u001b[0m, in \u001b[0;36mDuckDuckGoSearchAPIWrapper.results\u001b[1;34m(self, query, max_results, source)\u001b[0m\n\u001b[0;32m    142\u001b[0m source \u001b[38;5;241m=\u001b[39m source \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msource\n\u001b[0;32m    143\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m source \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtext\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m    144\u001b[0m     results \u001b[38;5;241m=\u001b[39m [\n\u001b[0;32m    145\u001b[0m         {\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msnippet\u001b[39m\u001b[38;5;124m\"\u001b[39m: r[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbody\u001b[39m\u001b[38;5;124m\"\u001b[39m], \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtitle\u001b[39m\u001b[38;5;124m\"\u001b[39m: r[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtitle\u001b[39m\u001b[38;5;124m\"\u001b[39m], \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mlink\u001b[39m\u001b[38;5;124m\"\u001b[39m: r[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhref\u001b[39m\u001b[38;5;124m\"\u001b[39m]}\n\u001b[1;32m--> 146\u001b[0m         \u001b[38;5;28;01mfor\u001b[39;00m r \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_ddgs_text\u001b[49m\u001b[43m(\u001b[49m\u001b[43mquery\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmax_results\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmax_results\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    147\u001b[0m     ]\n\u001b[0;32m    148\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m source \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mnews\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m    149\u001b[0m     results \u001b[38;5;241m=\u001b[39m [\n\u001b[0;32m    150\u001b[0m         {\n\u001b[0;32m    151\u001b[0m             \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msnippet\u001b[39m\u001b[38;5;124m\"\u001b[39m: r[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mbody\u001b[39m\u001b[38;5;124m\"\u001b[39m],\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    157\u001b[0m         \u001b[38;5;28;01mfor\u001b[39;00m r \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_ddgs_news(query, max_results\u001b[38;5;241m=\u001b[39mmax_results)\n\u001b[0;32m    158\u001b[0m     ]\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\langchain_community\\utilities\\duckduckgo_search.py:64\u001b[0m, in \u001b[0;36mDuckDuckGoSearchAPIWrapper._ddgs_text\u001b[1;34m(self, query, max_results)\u001b[0m\n\u001b[0;32m     61\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mduckduckgo_search\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m DDGS\n\u001b[0;32m     63\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m DDGS() \u001b[38;5;28;01mas\u001b[39;00m ddgs:\n\u001b[1;32m---> 64\u001b[0m     ddgs_gen \u001b[38;5;241m=\u001b[39m \u001b[43mddgs\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtext\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m     65\u001b[0m \u001b[43m        \u001b[49m\u001b[43mquery\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     66\u001b[0m \u001b[43m        \u001b[49m\u001b[43mregion\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mregion\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     67\u001b[0m \u001b[43m        \u001b[49m\u001b[43msafesearch\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msafesearch\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     68\u001b[0m \u001b[43m        \u001b[49m\u001b[43mtimelimit\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtime\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     69\u001b[0m \u001b[43m        \u001b[49m\u001b[43mmax_results\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmax_results\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01mor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mmax_results\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     70\u001b[0m \u001b[43m        \u001b[49m\u001b[43mbackend\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mbackend\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m     71\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m     72\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m ddgs_gen:\n\u001b[0;32m     73\u001b[0m         \u001b[38;5;28;01mreturn\u001b[39;00m [r \u001b[38;5;28;01mfor\u001b[39;00m r \u001b[38;5;129;01min\u001b[39;00m ddgs_gen]\n",
      "File \u001b[1;32mD:\\conda_envs\\rag_learn\\lib\\site-packages\\duckduckgo_search\\duckduckgo_search.py:198\u001b[0m, in \u001b[0;36mDDGS.text\u001b[1;34m(self, keywords, region, safesearch, timelimit, backend, max_results)\u001b[0m\n\u001b[0;32m    195\u001b[0m         logger\u001b[38;5;241m.\u001b[39minfo(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mError to search using \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mb\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m backend: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mex\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m    196\u001b[0m         err \u001b[38;5;241m=\u001b[39m ex\n\u001b[1;32m--> 198\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m DuckDuckGoSearchException(err)\n",
      "\u001b[1;31mDuckDuckGoSearchException\u001b[0m: https://www.bing.com/search?q=%E7%8E%B0%E4%BB%BB%E7%BE%8E%E5%9B%BD%E6%80%BB%E7%BB%9F%E6%98%AF%E8%B0%81%EF%BC%9F%E4%BB%96%E7%9A%84%E5%B9%B4%E9%BE%84%E6%98%AF%E5%A4%9A%E5%B0%91%EF%BC%9F&filters=ex1%3A%22ez5_19959_20324%22 return None. params={'q': '现任美国总统是谁？他的年龄是多少？', 'filters': 'ex1:\"ez5_19959_20324\"'} content=None data=None"
     ]
    }
   ],
   "source": [
    "\"\"\"第一个agent\n",
    "    会做数学题\n",
    "    不知道答案的时候可以搜索\n",
    "\"\"\"\n",
    "\"\"\"替代聚合搜索引擎serpai的推荐方案：\n",
    "    1.开发测试阶段：使用 DuckDuckGo（免费）\n",
    "    2.生产环境：使用 Tavily API（性价比高）\n",
    "    3.需要高质量结果：使用 Google Search API 或 Serper API\n",
    "\"\"\"\n",
    "from langchain.chat_models import ChatOpenAI\n",
    "from langchain.agents import load_tools, initialize_agent, AgentType\n",
    "from langchain_community.tools import DuckDuckGoSearchResults\n",
    "import os\n",
    "\n",
    "llm = ChatOpenAI(\n",
    "    model=\"qwen-plus\",\n",
    "    temperature=0,\n",
    "    openai_api_key=\"sk-4e88cf4db3e14894bafaff606d296610\",\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    ")\n",
    "\n",
    "# 只加载数学工具\n",
    "tools = load_tools([\"llm-math\"], llm=llm)\n",
    "\n",
    "# 添加 DuckDuckGo 搜索\n",
    "search_tool = DuckDuckGoSearchResults()\n",
    "tools.append(search_tool)\n",
    "\n",
    "agent = initialize_agent(\n",
    "    tools,\n",
    "    llm,\n",
    "    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,\n",
    "    verbose=True,\n",
    ")\n",
    "\n",
    "agent.invoke(\"请问现任的美国总统是谁？他的年龄的平方是多少?\")\n",
    "\n",
    "# 上面的代码在华为云主机上执行正常！"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "rag_learn",
   "language": "python",
   "name": "rag_learn"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
